Set working directory and load necessary packages.
Load the data for March. Load each file
dir('./March 18')
[1] "Combined_March.csv" "March_1.csv" "March_10.csv" "March_11.csv"
[5] "March_12.csv" "March_18" "March_2.csv" "March_3.csv"
[9] "March_4.csv" "March_5.csv" "March_6.csv" "March_7.csv"
[13] "March_8.csv" "March_9.csv"
path <- './March 18'
### list out all the csv files that start with TT_ and then have a number. This will avoid loading in the unwanted aggregated files or VMT files.
###'^_\\d.' returns all files that end with _some_digit.csv.
list_csv <- dir(path=path,pattern = '*_\\d.csv')
myfiles <- lapply(paste(path,'/',list_csv,sep=''),
function(x) read.csv(x,stringsAsFactors = FALSE))
files <- mapply(cbind,myfiles,'filename' = list_csv,SIMPLIFY = F)
dat <- bind_rows(files)
rm(myfiles,files)
#create a dataset that we will edit....
March_18_dat <- dat
colnames(March_18_dat)
[1] "X" "X.1" "X.2" "X12.15.AM" "X12.30.AM" "X12.45.AM" "X1.AM"
[8] "X1.15.AM" "X1.30.AM" "X1.45.AM" "X2.AM" "X2.15.AM" "X2.30.AM" "X2.45.AM"
[15] "X3.AM" "X3.15.AM" "X3.30.AM" "X3.45.AM" "X4.AM" "X4.15.AM" "X4.30.AM"
[22] "X4.45.AM" "X5.AM" "X5.15.AM" "X5.30.AM" "X5.45.AM" "X6.AM" "X6.15.AM"
[29] "X6.30.AM" "X6.45.AM" "X7.AM" "X7.15.AM" "X7.30.AM" "X7.45.AM" "X8.AM"
[36] "X8.15.AM" "X8.30.AM" "X8.45.AM" "X9.AM" "X9.15.AM" "X9.30.AM" "X9.45.AM"
[43] "X10.AM" "X10.15.AM" "X10.30.AM" "X10.45.AM" "X11.AM" "X11.15.AM" "X11.30.AM"
[50] "X11.45.AM" "Noon" "X12.15.PM" "X12.30.PM" "X12.45.PM" "X1.PM" "X1.15.PM"
[57] "X1.30.PM" "X1.45.PM" "X2.PM" "X2.15.PM" "X2.30.PM" "X2.45.PM" "X3.PM"
[64] "X3.15.PM" "X3.30.PM" "X3.45.PM" "X4.PM" "X4.15.PM" "X4.30.PM" "X4.45.PM"
[71] "X5.PM" "X5.15.PM" "X5.30.PM" "X5.45.PM" "X6.PM" "X6.15.PM" "X6.30.PM"
[78] "X6.45.PM" "X7.PM" "X7.15.PM" "X7.30.PM" "X7.45.PM" "X8.PM" "X8.15.PM"
[85] "X8.30.PM" "X8.45.PM" "X9.PM" "X9.15.PM" "X9.30.PM" "X9.45.PM" "X10.PM"
[92] "X10.15.PM" "X10.30.PM" "X10.45.PM" "X11.PM" "X11.15.PM" "X11.30.PM" "X11.45.PM"
[99] "Midnight" "X.3" "filename"
Lets fix the column headers first. There are no headers for the first 3 columns and also there appears to be a column at the end of the data frame “X.3”
summary(March_18_dat$X.3)
Mode NA's
logical 39990
All NAs…. remove.
March_18_dat <- March_18_dat[,!names(March_18_dat) == "X.3"]
Focus on the first three columns
str(March_18_dat[1:3])
'data.frame': 39990 obs. of 3 variables:
$ X : int 1180 1180 1180 1180 1180 1180 1180 1180 1180 1180 ...
$ X.1: chr "Density" "Density" "Density" "Density" ...
$ X.2: chr "2018/03/01" "2018/03/02" "2018/03/03" "2018/03/04" ...
Column 1 is the detector ID Column 2 is the metric Column 3 is the date.
colnames(March_18_dat)[1:3] <- c('Detector_ID','Metric','Date')
Now lets change the data types in the columns
March_18_dat$Metric <- as.factor(March_18_dat$Metric)
March_18_dat$Date_Posixct <- as.POSIXct(March_18_dat$Date,format="%Y/%m/%d")
March_18_dat$Detector_ID <- as.factor(March_18_dat$Detector_ID)
## Check that our dates worked out. We don't want any NAs.
table(March_18_dat$Date_Posixct)
2018-03-01 2018-03-02 2018-03-03 2018-03-04 2018-03-05 2018-03-06 2018-03-07 2018-03-08
1290 1290 1290 1290 1290 1290 1290 1290
2018-03-09 2018-03-10 2018-03-11 2018-03-12 2018-03-13 2018-03-14 2018-03-15 2018-03-16
1290 1290 1290 1290 1290 1290 1290 1290
2018-03-17 2018-03-18 2018-03-19 2018-03-20 2018-03-21 2018-03-22 2018-03-23 2018-03-24
1290 1290 1290 1290 1290 1290 1290 1290
2018-03-25 2018-03-26 2018-03-27 2018-03-28 2018-03-29 2018-03-30 2018-03-31
1290 1290 1290 1290 1290 1290 1290
There noon and midnight are labelled as strings. The rest of the columns are labelled as times formats (in strings). Lets change noon and midnight to the same format as the other times.
colnames(March_18_dat)[colnames(March_18_dat)%in%
c('X1.AM','X2.AM','X3.AM','X4.AM','X5.AM','X6.AM',
'X7.AM','X8.AM','X9.AM','X10.AM','X11.AM',
'X1.PM','X2.PM','X3.PM','X4.PM','X5.PM','X6.PM',
'X7.PM','X8.PM','X9.PM','X10.PM','X11.PM','Noon','Midnight')] <-
c('X1.00.AM','X2.00.AM','X3.00.AM','X4.00.AM','X5.00.AM','X6.00.AM',
'X7.00.AM','X8.00.AM','X9.00.AM','X10.00.AM','X11.00.AM',
'X1.00.PM','X2.00.PM','X3.00.PM','X4.00.PM','X5.00.PM','X6.00.PM',
'X7.00.PM','X8.00.PM','X9.00.PM','X10.00.PM','X11.00.PM','X00.00.AM','X00.00.PM')
March_18_dat <- March_18_dat %>% select(Detector_ID,Metric,Date,Date_Posixct,filename,everything())
Melt the data
March_18_melt <- melt(March_18_dat,id.vars = c('Detector_ID','Metric','Date','Date_Posixct','filename'),variable.name = "Time",value.name = 'Metric_value')
Create Date_Time variable
#remove X
March_18_melt$Time <- gsub('X','',March_18_melt$Time)
March_18_melt$Date_Time <- as.POSIXct(paste(March_18_melt$Date,March_18_melt$Time),format="%Y/%m/%d %I.%M.%p")
March_18_melt$Hour <- hour(March_18_melt$Date_Time)
Filter to the PM Peak hours, 2pm to 7pm.
PM_Peak <- c(14:19)
March_18_melt_PM_Peak <- March_18_melt %>%
filter(Hour>=14)%>%
filter(Hour<=19)
Lets find what detectors have bad values.
var <- March_18_melt_PM_Peak %>%
group_by(Detector_ID)%>%
summarise(NAs =sum(Metric_value<0))%>%
arrange(NAs)%>%
filter(NAs==0)%>%
droplevels()
no_NAs <- as.vector(var$Detector_ID)
Time Series
Lets pick Detector 70. We want to create a variable for each day and them average the volumes for each day.
We also only want Tuesday, Wednesday and Thursday.
Det_70 <- March_18_melt_PM_Peak %>%
filter(Detector_ID==150)%>%
filter(Metric=="Volume")%>%
mutate(YearDay=yday(Date_Time),
Weekday=weekdays(Date_Time,abbreviate=TRUE))%>%
# filter(Weekday %in% c('Tue','Wed','Thu'))%>%
group_by(Date_Posixct)%>%
summarise(SumVol = sum(Metric_value))
head(Det_70)
Now we create a time series for the data
Det_70_ts_xts <- xts(Det_70$SumVol,order.by = Det_70$Date_Posixct)
dygraph(xts(Det_70$SumVol,order.by = Det_70$Date_Posixct))%>%
dySeries(label = 'Total Vol.')%>%
dyRangeSelector()
Det_70_ts <- ts(Det_70$SumVol,frequency=3)
Det_70_ts_deomp <- decompose(Det_70_ts)
Det_70_ts_stl <- stl(Det_70_ts,s.window = 'periodic')
midweek <- c('Tuesday','Wednesday','Thursday')
day_select <- cbind(data.frame(Det_70_ts_stl$time.series[,1:3]),Date=Det_70$Date_Posixct,Weekday=weekdays(Det_70$Date_Posixct))%>%
# mutate(Resid = abs(remainder))%>%
filter(Weekday %in% midweek)%>%
arrange(remainder)%>%
print()
day_select$Date[day_select$remainder==median(day_select$remainder)]
[1] "2018-03-29 CDT"
FM_TS_F$Date[FM_TS_F$remainder==median(FM_TS_F$remainder)]
Error: object 'FM_TS_F' not found
Day selection is Tuesday 2018-03-13.
Correlation
Show plots of volumes by day
y <- March_18_melt_PM_Peak %>%
mutate(Weekday = weekdays(Date_Time,abbreviate=TRUE),
YearDay=yday(Date_Time),
MonthDay=day(Date_Time),
HourMin = as.numeric(format(Date_Time,"%H.%M")))%>%
filter(Metric=='Volume')%>%
filter(Detector_ID%in%c(147:151))%>%
filter(Weekday %in% c('Tue','Wed','Thu'))
ggplot(y,aes(x=Metric_value,color=as.factor(Detector_ID)))+
geom_density()+facet_wrap(~MonthDay,nrow=4,scales = 'free_x')
Create detector ~ day matrix
d3heatmap(matrix2,scale = 'row',Colv = 'as-is',colors = 'Blues')
Invalid value for Colv, ignoring
Cluster Days
day_SDev <- March_18_melt_PM_Peak %>%
mutate(Weekday = weekdays(Date_Time,abbreviate=TRUE),
YearDay=yday(Date_Time),
MonthDay=day(Date_Time),
HourMin = as.numeric(format(Date_Time,"%H.%M")))%>%
filter(Metric=='Volume')%>%
group_by(Detector_ID,Date_Posixct)%>%
summarise(SumVol = sum(Metric_value))%>%
group_by(Date_Posixct)%>%
summarise(SDev = sd(SumVol))%>%
mutate(weekDay=weekdays(Date_Posixct,abbreviate=TRUE))
sundays <-day_SDev$Date_Posixct[day_SDev$weekDay%in%c('Sun')]
ggplot(day_SDev) +
geom_point(aes(Date_Posixct,SDev)) +
geom_rug(aes(Date_Posixct,SDev)) +
geom_vline(xintercept = sundays,linetype='dotted') +
theme_tufte(ticks = F) +
xlab("STD of the total volume across all Detectors") +
ylab("Date") +
theme(axis.title.x = element_text(vjust=-0.5), axis.title.y = element_text(vjust=1))
Cumulative running total
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpTZXQgd29ya2luZyBkaXJlY3RvcnkgYW5kIGxvYWQgbmVjZXNzYXJ5IHBhY2thZ2VzLg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIkg6L1Byb2plY3RzLzExMDAwLzExMTU1L1RyYWZmU3R1ZHkvRGF0YUNvbGxlY3Rpb24vRnJlZXdheURhdGEvRGV0ZWN0b3IgRGF0YS9Wb2x1bWUgRGF0YSIpDQoNCiMjYmVsb3cgYXJlIGEgbGlzdCBvZiBwYWNrYWdlcyByZXF1aXJlZCB0byBydW4gdGhlIG1hcmtkb3duIGZpbGUNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KHpvbykNCmxpYnJhcnkoaW1wdXRlVFMpDQpsaWJyYXJ5KHh0cykNCmxpYnJhcnkoZHlncmFwaHMpDQpsaWJyYXJ5KGQzaGVhdG1hcCkNCmxpYnJhcnkoZ2d0aGVtZXMpDQpsaWJyYXJ5KHBsb3RseSkNCg0KYGBgDQoNCkxvYWQgdGhlIGRhdGEgZm9yIE1hcmNoLiBMb2FkIGVhY2ggZmlsZQ0KDQpgYGB7ciBleHBsb3JlIE1hcmNofQ0KDQpkaXIoJy4vTWFyY2ggMTgnKQ0KDQpgYGANCg0KYGBge3IgbG9hZCBkYXRhIGZvciBNYXJjaCx3YXJuaW5nPUZBTFNFfQ0KcGF0aCA8LSAnLi9NYXJjaCAxOCcNCg0KIyMjIGxpc3Qgb3V0IGFsbCB0aGUgY3N2IGZpbGVzIHRoYXQgc3RhcnQgd2l0aCBUVF8gYW5kIHRoZW4gaGF2ZSBhIG51bWJlci4gVGhpcyB3aWxsIGF2b2lkIGxvYWRpbmcgaW4gdGhlIHVud2FudGVkIGFnZ3JlZ2F0ZWQgZmlsZXMgb3IgVk1UIGZpbGVzLg0KIyMjJ15fXFxkLicgcmV0dXJucyBhbGwgZmlsZXMgdGhhdCBlbmQgd2l0aCBfc29tZV9kaWdpdC5jc3YuDQpsaXN0X2NzdiA8LSBkaXIocGF0aD1wYXRoLHBhdHRlcm4gPSAnKl9cXGQuY3N2JykNCg0KbXlmaWxlcyA8LSBsYXBwbHkocGFzdGUocGF0aCwnLycsbGlzdF9jc3Ysc2VwPScnKSwNCiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHJlYWQuY3N2KHgsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSkNCg0KZmlsZXMgPC0gbWFwcGx5KGNiaW5kLG15ZmlsZXMsJ2ZpbGVuYW1lJyA9IGxpc3RfY3N2LFNJTVBMSUZZID0gRikNCg0KZGF0IDwtIGJpbmRfcm93cyhmaWxlcykNCg0Kcm0obXlmaWxlcyxmaWxlcykNCg0KI2NyZWF0ZSBhIGRhdGFzZXQgdGhhdCB3ZSB3aWxsIGVkaXQuLi4uDQpNYXJjaF8xOF9kYXQgPC0gZGF0DQpgYGANCg0KDQoNCmBgYHtyIGV4YW1pbmUgY29sdW1uc30NCg0KY29sbmFtZXMoTWFyY2hfMThfZGF0KQ0KYGBgDQoNCkxldHMgZml4IHRoZSBjb2x1bW4gaGVhZGVycyBmaXJzdC4gVGhlcmUgYXJlIG5vIGhlYWRlcnMgZm9yIHRoZSBmaXJzdCAzIGNvbHVtbnMgYW5kIGFsc28gdGhlcmUgYXBwZWFycyB0byBiZSBhIGNvbHVtbiBhdCB0aGUgZW5kIG9mIHRoZSBkYXRhIGZyYW1lICJYLjMiDQoNCmBgYHtyIHN1eW1tYXJ5IG9mIGRhdGFzZXR9DQpzdW1tYXJ5KE1hcmNoXzE4X2RhdCRYLjMpDQpgYGANCg0KQWxsIE5Bcy4uLi4gcmVtb3ZlLg0KDQpgYGB7ciByZW1vdmUgYmxhbmsgbGFzdCBjb2x1bW59DQoNCk1hcmNoXzE4X2RhdCA8LSBNYXJjaF8xOF9kYXRbLCFuYW1lcyhNYXJjaF8xOF9kYXQpID09ICJYLjMiXQ0KDQpgYGANCg0KRm9jdXMgb24gdGhlIGZpcnN0IHRocmVlIGNvbHVtbnMNCg0KYGBge3IgZXhhbWluZSBmaXJzdCAzIHJvd3N9DQpzdHIoTWFyY2hfMThfZGF0WzE6M10pDQpgYGANCg0KQ29sdW1uIDEgaXMgdGhlIGRldGVjdG9yIElEDQpDb2x1bW4gMiBpcyB0aGUgbWV0cmljDQpDb2x1bW4gMyBpcyB0aGUgZGF0ZS4NCg0KDQpgYGB7ciBjaGFuZ2UgZmlyc3QgMyBjb2x1bW4gbmFtZXN9DQoNCmNvbG5hbWVzKE1hcmNoXzE4X2RhdClbMTozXSA8LSBjKCdEZXRlY3Rvcl9JRCcsJ01ldHJpYycsJ0RhdGUnKQ0KDQpgYGANCg0KTm93IGxldHMgY2hhbmdlIHRoZSBkYXRhIHR5cGVzIGluIHRoZSBjb2x1bW5zDQoNCmBgYHtyIG1ldHJpYyBhbmQgRGF0ZV9wb3NpeGN0fQ0KDQpNYXJjaF8xOF9kYXQkTWV0cmljIDwtIGFzLmZhY3RvcihNYXJjaF8xOF9kYXQkTWV0cmljKQ0KTWFyY2hfMThfZGF0JERhdGVfUG9zaXhjdCA8LSBhcy5QT1NJWGN0KE1hcmNoXzE4X2RhdCREYXRlLGZvcm1hdD0iJVkvJW0vJWQiKQ0KTWFyY2hfMThfZGF0JERldGVjdG9yX0lEIDwtIGFzLmZhY3RvcihNYXJjaF8xOF9kYXQkRGV0ZWN0b3JfSUQpDQojIyBDaGVjayB0aGF0IG91ciBkYXRlcyB3b3JrZWQgb3V0LiBXZSBkb24ndCB3YW50IGFueSBOQXMuDQp0YWJsZShNYXJjaF8xOF9kYXQkRGF0ZV9Qb3NpeGN0KQ0KDQpgYGANCg0KVGhlcmUgbm9vbiBhbmQgbWlkbmlnaHQgYXJlIGxhYmVsbGVkIGFzIHN0cmluZ3MuIFRoZSByZXN0IG9mIHRoZSBjb2x1bW5zIGFyZSBsYWJlbGxlZCBhcyB0aW1lcyBmb3JtYXRzIChpbiBzdHJpbmdzKS4gTGV0cyBjaGFuZ2Ugbm9vbiBhbmQgbWlkbmlnaHQgdG8gdGhlIHNhbWUgZm9ybWF0IGFzIHRoZSBvdGhlciB0aW1lcy4NCg0KYGBge3IgY2hhbmdlIG5vb24gbWlkbmlnaHQgbmFtZXN9DQoNCmNvbG5hbWVzKE1hcmNoXzE4X2RhdClbY29sbmFtZXMoTWFyY2hfMThfZGF0KSVpbiUNCiAgICAgICAgICAgICAgICAgICAgICAgICBjKCdYMS5BTScsJ1gyLkFNJywnWDMuQU0nLCdYNC5BTScsJ1g1LkFNJywnWDYuQU0nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1g3LkFNJywnWDguQU0nLCdYOS5BTScsJ1gxMC5BTScsJ1gxMS5BTScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAnWDEuUE0nLCdYMi5QTScsJ1gzLlBNJywnWDQuUE0nLCdYNS5QTScsJ1g2LlBNJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICdYNy5QTScsJ1g4LlBNJywnWDkuUE0nLCdYMTAuUE0nLCdYMTEuUE0nLCdOb29uJywnTWlkbmlnaHQnKV0gPC0NCiAgYygnWDEuMDAuQU0nLCdYMi4wMC5BTScsJ1gzLjAwLkFNJywnWDQuMDAuQU0nLCdYNS4wMC5BTScsJ1g2LjAwLkFNJywNCiAgICAnWDcuMDAuQU0nLCdYOC4wMC5BTScsJ1g5LjAwLkFNJywnWDEwLjAwLkFNJywnWDExLjAwLkFNJywNCiAgICAnWDEuMDAuUE0nLCdYMi4wMC5QTScsJ1gzLjAwLlBNJywnWDQuMDAuUE0nLCdYNS4wMC5QTScsJ1g2LjAwLlBNJywNCiAgICAnWDcuMDAuUE0nLCdYOC4wMC5QTScsJ1g5LjAwLlBNJywnWDEwLjAwLlBNJywnWDExLjAwLlBNJywnWDAwLjAwLkFNJywnWDAwLjAwLlBNJykNCg0KTWFyY2hfMThfZGF0IDwtIE1hcmNoXzE4X2RhdCAlPiUgc2VsZWN0KERldGVjdG9yX0lELE1ldHJpYyxEYXRlLERhdGVfUG9zaXhjdCxmaWxlbmFtZSxldmVyeXRoaW5nKCkpDQoNCg0KYGBgDQoNCk1lbHQgdGhlIGRhdGENCg0KYGBge3IgbWVsdH0NCg0KTWFyY2hfMThfbWVsdCA8LSBtZWx0KE1hcmNoXzE4X2RhdCxpZC52YXJzID0gYygnRGV0ZWN0b3JfSUQnLCdNZXRyaWMnLCdEYXRlJywnRGF0ZV9Qb3NpeGN0JywnZmlsZW5hbWUnKSx2YXJpYWJsZS5uYW1lID0gIlRpbWUiLHZhbHVlLm5hbWUgPSAnTWV0cmljX3ZhbHVlJykNCg0KYGBgDQoNCkNyZWF0ZSBEYXRlX1RpbWUgdmFyaWFibGUNCg0KYGBge3IgRGF0ZV9UaW1lfQ0KDQojcmVtb3ZlIFgNCk1hcmNoXzE4X21lbHQkVGltZSA8LSBnc3ViKCdYJywnJyxNYXJjaF8xOF9tZWx0JFRpbWUpDQoNCk1hcmNoXzE4X21lbHQkRGF0ZV9UaW1lIDwtIGFzLlBPU0lYY3QocGFzdGUoTWFyY2hfMThfbWVsdCREYXRlLE1hcmNoXzE4X21lbHQkVGltZSksZm9ybWF0PSIlWS8lbS8lZCAlSS4lTS4lcCIpDQoNCk1hcmNoXzE4X21lbHQkSG91ciA8LSBob3VyKE1hcmNoXzE4X21lbHQkRGF0ZV9UaW1lKQ0KDQpgYGANCg0KRmlsdGVyIHRvIHRoZSBQTSBQZWFrIGhvdXJzLCAycG0gdG8gN3BtLg0KDQpgYGB7ciBQZWFrIEhvdXJzfQ0KDQpQTV9QZWFrIDwtIGMoMTQ6MTkpDQoNCk1hcmNoXzE4X21lbHRfUE1fUGVhayA8LSBNYXJjaF8xOF9tZWx0ICU+JQ0KICBmaWx0ZXIoSG91cj49MTQpJT4lDQogIGZpbHRlcihIb3VyPD0xOSkNCg0KYGBgDQoNCkxldHMgZmluZCB3aGF0IGRldGVjdG9ycyBoYXZlIGJhZCB2YWx1ZXMuIA0KDQpgYGB7ciBOQSB2YWx1ZXN9DQoNCnZhciA8LSBNYXJjaF8xOF9tZWx0X1BNX1BlYWsgJT4lIA0KICBncm91cF9ieShEZXRlY3Rvcl9JRCklPiUNCiAgc3VtbWFyaXNlKE5BcyA9c3VtKE1ldHJpY192YWx1ZTwwKSklPiUNCiAgYXJyYW5nZShOQXMpJT4lDQogIGZpbHRlcihOQXM9PTApJT4lDQogIGRyb3BsZXZlbHMoKQ0KICANCm5vX05BcyA8LSBhcy52ZWN0b3IodmFyJERldGVjdG9yX0lEKQ0KDQpgYGANCg0KIyNUaW1lIFNlcmllcw0KDQpMZXRzIHBpY2sgRGV0ZWN0b3IgNzAuIFdlIHdhbnQgdG8gY3JlYXRlIGEgdmFyaWFibGUgZm9yIGVhY2ggZGF5IGFuZCB0aGVtIGF2ZXJhZ2UgdGhlIHZvbHVtZXMgZm9yIGVhY2ggZGF5Lg0KDQpXZSBhbHNvIG9ubHkgd2FudCBUdWVzZGF5LCBXZWRuZXNkYXkgYW5kIFRodXJzZGF5LiANCg0KYGBge3IgZGV0XzcwfQ0KDQpEZXRfNzAgPC0gIE1hcmNoXzE4X21lbHRfUE1fUGVhayAlPiUNCiAgZmlsdGVyKERldGVjdG9yX0lEPT0xNTApJT4lDQogIGZpbHRlcihNZXRyaWM9PSJWb2x1bWUiKSU+JQ0KICBtdXRhdGUoWWVhckRheT15ZGF5KERhdGVfVGltZSksDQogICAgICAgICBXZWVrZGF5PXdlZWtkYXlzKERhdGVfVGltZSxhYmJyZXZpYXRlPVRSVUUpKSU+JQ0KICAjIGZpbHRlcihXZWVrZGF5ICVpbiUgYygnVHVlJywnV2VkJywnVGh1JykpJT4lDQogIGdyb3VwX2J5KERhdGVfUG9zaXhjdCklPiUNCiAgc3VtbWFyaXNlKFN1bVZvbCA9IHN1bShNZXRyaWNfdmFsdWUpKQ0KDQpoZWFkKERldF83MCkNCmBgYA0KDQoNCk5vdyB3ZSBjcmVhdGUgYSB0aW1lIHNlcmllcyBmb3IgdGhlIGRhdGENCg0KYGBge3IgVGltZSBTZXJpZXMsZmlnLndpZHRoPTksZmlnLmhlaWdodD01fQ0KDQpEZXRfNzBfdHNfeHRzIDwtIHh0cyhEZXRfNzAkU3VtVm9sLG9yZGVyLmJ5ID0gRGV0XzcwJERhdGVfUG9zaXhjdCkNCg0KZHlncmFwaCh4dHMoRGV0XzcwJFN1bVZvbCxvcmRlci5ieSA9IERldF83MCREYXRlX1Bvc2l4Y3QpKSU+JQ0KICBkeVNlcmllcyhsYWJlbCA9ICdUb3RhbCBWb2wuJyklPiUNCiAgZHlSYW5nZVNlbGVjdG9yKCkNCmBgYA0KDQpgYGB7ciBkZWNvbXBvc2UgZGF0YX0NCkRldF83MF90cyA8LSB0cyhEZXRfNzAkU3VtVm9sLGZyZXF1ZW5jeT0zKQ0KDQpEZXRfNzBfdHNfZGVvbXAgPC0gZGVjb21wb3NlKERldF83MF90cykNCg0KRGV0XzcwX3RzX3N0bCA8LSBzdGwoRGV0XzcwX3RzLHMud2luZG93ID0gJ3BlcmlvZGljJykNCg0KbWlkd2VlayA8LSBjKCdUdWVzZGF5JywnV2VkbmVzZGF5JywnVGh1cnNkYXknKQ0KDQpkYXlfc2VsZWN0IDwtICBjYmluZChkYXRhLmZyYW1lKERldF83MF90c19zdGwkdGltZS5zZXJpZXNbLDE6M10pLERhdGU9RGV0XzcwJERhdGVfUG9zaXhjdCxXZWVrZGF5PXdlZWtkYXlzKERldF83MCREYXRlX1Bvc2l4Y3QpKSU+JQ0KICAjIG11dGF0ZShSZXNpZCA9IGFicyhyZW1haW5kZXIpKSU+JQ0KICBmaWx0ZXIoV2Vla2RheSAlaW4lIG1pZHdlZWspJT4lDQogIGFycmFuZ2UocmVtYWluZGVyKSU+JQ0KICBwcmludCgpDQoNCmRheV9zZWxlY3QkRGF0ZVtkYXlfc2VsZWN0JHJlbWFpbmRlcj09bWVkaWFuKGRheV9zZWxlY3QkcmVtYWluZGVyKV0NCmBgYA0KDQoNCmBgYHtyfQ0KRk1fVFNfRiREYXRlW0ZNX1RTX0YkcmVtYWluZGVyPT1tZWRpYW4oRk1fVFNfRiRyZW1haW5kZXIpXQ0KYGBgDQoNCg0KRGF5IHNlbGVjdGlvbiBpcyBgciBwYXN0ZShkYXlfc2VsZWN0JFdlZWtkYXlbMV0sZGF5X3NlbGVjdCREYXRlWzFdKWAuDQoNCiMjIENvcnJlbGF0aW9uDQoNClNob3cgcGxvdHMgb2Ygdm9sdW1lcyBieSBkYXkNCg0KYGBge3IgcGxvdCB2YWx1ZXN9DQp5IDwtIE1hcmNoXzE4X21lbHRfUE1fUGVhayAlPiUNCiAgbXV0YXRlKFdlZWtkYXkgPSB3ZWVrZGF5cyhEYXRlX1RpbWUsYWJicmV2aWF0ZT1UUlVFKSwNCiAgICAgICAgIFllYXJEYXk9eWRheShEYXRlX1RpbWUpLA0KICAgICAgICAgTW9udGhEYXk9ZGF5KERhdGVfVGltZSksDQogICAgICAgICBIb3VyTWluID0gYXMubnVtZXJpYyhmb3JtYXQoRGF0ZV9UaW1lLCIlSC4lTSIpKSklPiUNCiAgZmlsdGVyKE1ldHJpYz09J1ZvbHVtZScpJT4lDQogIGZpbHRlcihEZXRlY3Rvcl9JRCVpbiVjKDE0NzoxNTEpKSU+JQ0KICBmaWx0ZXIoV2Vla2RheSAlaW4lIGMoJ1R1ZScsJ1dlZCcsJ1RodScpKQ0KDQogIGdncGxvdCh5LGFlcyh4PU1ldHJpY192YWx1ZSxjb2xvcj1hcy5mYWN0b3IoRGV0ZWN0b3JfSUQpKSkrDQogIGdlb21fZGVuc2l0eSgpK2ZhY2V0X3dyYXAofk1vbnRoRGF5LG5yb3c9NCxzY2FsZXMgPSAnZnJlZV94JykNCmBgYA0KDQoNCkNyZWF0ZSBkZXRlY3RvciB+IGRheSBtYXRyaXgNCg0KYGBge3IgaGVhdG1hcCBmb3Igc3Vic2V0LGZpZy53aWR0aD0xMX0NCg0KbGV2ZWxzIDwtIGxhc3QobGV2ZWxzKE1hcmNoXzE4X21lbHRfUE1fUGVhayREZXRlY3Rvcl9JRCksMTAwKQ0KDQptYXRyaXggPC0gTWFyY2hfMThfbWVsdF9QTV9QZWFrICU+JQ0KICBtdXRhdGUoV2Vla2RheSA9IHdlZWtkYXlzKERhdGVfVGltZSxhYmJyZXZpYXRlPVRSVUUpLA0KICAgICAgICAgWWVhckRheT15ZGF5KERhdGVfVGltZSksDQogICAgICAgICBNb250aERheT1kYXkoRGF0ZV9UaW1lKSwNCiAgICAgICAgIEhvdXJNaW4gPSBhcy5udW1lcmljKGZvcm1hdChEYXRlX1RpbWUsIiVIIikpKw0KICAgICAgICAgICBhcy5udW1lcmljKGZvcm1hdChEYXRlX1RpbWUsIiVNIikpLzYwKSU+JQ0KICBmaWx0ZXIoTWV0cmljPT0nVm9sdW1lJyklPiUNCiAgZmlsdGVyKERldGVjdG9yX0lEICVpbiUgbGV2ZWxzKSU+JQ0KICBmaWx0ZXIoRGV0ZWN0b3JfSUQgJWluJSBub19OQXMpJT4lDQogIGdyb3VwX2J5KERhdGVfUG9zaXhjdCxEZXRlY3Rvcl9JRCklPiUNCiAgYXJyYW5nZShEYXRlX1RpbWUpJT4lDQogIG11dGF0ZShjdW1TdW0gPSBjdW1zdW0oTWV0cmljX3ZhbHVlKSklPiUNCiAgbXV0YXRlKGNvcnJlbGF0aW9uID0gbG0oY3VtU3VtfkhvdXJNaW4pJGNvZWZmaWNpZW50c1syXSklPiUNCiAgYXJyYW5nZShEYXRlX1Bvc2l4Y3QpDQoNCg0KbWF0cml4X3NwcmVhZCA8LSBtYXRyaXglPiUNCiAgZ3JvdXBfYnkoRGF0ZV9Qb3NpeGN0LERldGVjdG9yX0lEKSU+JQ0KICBzdW1tYXJpc2UoY29ycmVsYXRpb249Zmlyc3QoY29ycmVsYXRpb24pKSU+JQ0KICBzcHJlYWQoRGF0ZV9Qb3NpeGN0LGNvcnJlbGF0aW9uKQ0KDQoNCnZlY3RvciA8LSBhcy5jaGFyYWN0ZXIobWF0cml4X3NwcmVhZCREZXRlY3Rvcl9JRCkNCm1hdHJpeDIgPC0gbWF0cml4X3NwcmVhZFssLTFdICU+JSBhcy5kYXRhLmZyYW1lKCkNCnJvd25hbWVzKG1hdHJpeDIpIDwtIHZlY3Rvcg0KZDNoZWF0bWFwKG1hdHJpeDIsc2NhbGUgPSAncm93JyxDb2x2ID0gJ2FzLWlzJyxjb2xvcnMgPSAnQmx1ZXMnKQ0KYGBgDQoNCkNsdXN0ZXIgRGF5cw0KDQpgYGB7cn0NCg0KZGF5X1NEZXYgPC0gTWFyY2hfMThfbWVsdF9QTV9QZWFrICU+JQ0KICBtdXRhdGUoV2Vla2RheSA9IHdlZWtkYXlzKERhdGVfVGltZSxhYmJyZXZpYXRlPVRSVUUpLA0KICAgICAgICAgWWVhckRheT15ZGF5KERhdGVfVGltZSksDQogICAgICAgICBNb250aERheT1kYXkoRGF0ZV9UaW1lKSwNCiAgICAgICAgIEhvdXJNaW4gPSBhcy5udW1lcmljKGZvcm1hdChEYXRlX1RpbWUsIiVILiVNIikpKSU+JQ0KICBmaWx0ZXIoTWV0cmljPT0nVm9sdW1lJyklPiUNCiAgZ3JvdXBfYnkoRGV0ZWN0b3JfSUQsRGF0ZV9Qb3NpeGN0KSU+JQ0KICBzdW1tYXJpc2UoU3VtVm9sID0gc3VtKE1ldHJpY192YWx1ZSkpJT4lDQogIGdyb3VwX2J5KERhdGVfUG9zaXhjdCklPiUNCiAgc3VtbWFyaXNlKFNEZXYgPSBzZChTdW1Wb2wpKSU+JQ0KICBtdXRhdGUod2Vla0RheT13ZWVrZGF5cyhEYXRlX1Bvc2l4Y3QsYWJicmV2aWF0ZT1UUlVFKSkNCg0Kc3VuZGF5cyA8LWRheV9TRGV2JERhdGVfUG9zaXhjdFtkYXlfU0RldiR3ZWVrRGF5JWluJWMoJ1N1bicpXQ0KDQpnZ3Bsb3QoZGF5X1NEZXYpICsgDQogIGdlb21fcG9pbnQoYWVzKERhdGVfUG9zaXhjdCxTRGV2KSkgKyANCiAgZ2VvbV9ydWcoYWVzKERhdGVfUG9zaXhjdCxTRGV2KSkgKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gc3VuZGF5cyxsaW5ldHlwZT0nZG90dGVkJykgKw0KICB0aGVtZV90dWZ0ZSh0aWNrcyA9IEYpICsNCiAgeGxhYigiU1REIG9mIHRoZSB0b3RhbCB2b2x1bWUgYWNyb3NzIGFsbCBEZXRlY3RvcnMiKSArIA0KICB5bGFiKCJEYXRlIikgKyANCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHZqdXN0PS0wLjUpLCBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQodmp1c3Q9MSkpDQoNCmBgYA0KDQpDdW11bGF0aXZlIHJ1bm5pbmcgdG90YWwNCg0KYGBge3IgY3Vtc3VtLCBmaWcud2lkdGg9OX0NCg0KZyA8LSBNYXJjaF8xOF9tZWx0X1BNX1BlYWsgJT4lDQogIG11dGF0ZShXZWVrZGF5ID0gd2Vla2RheXMoRGF0ZV9UaW1lLGFiYnJldmlhdGU9VFJVRSksDQogICAgICAgICBZZWFyRGF5PXlkYXkoRGF0ZV9UaW1lKSwNCiAgICAgICAgIE1vbnRoRGF5PWRheShEYXRlX1RpbWUpLA0KICAgICAgICAgSG91ck1pbiA9IGFzLm51bWVyaWMoZm9ybWF0KERhdGVfVGltZSwiJUgiKSkrDQogICAgICAgICAgIGFzLm51bWVyaWMoZm9ybWF0KERhdGVfVGltZSwiJU0iKSkvNjApJT4lDQogIGZpbHRlcihEZXRlY3Rvcl9JRD09NzEwNSAmIE1ldHJpYz09J1ZvbHVtZScpJT4lDQogICMgZmlsdGVyKFllYXJEYXk9PTY1ICYgTWV0cmljPT0nVm9sdW1lJyklPiUNCiAgIyBmaWx0ZXIoRGV0ZWN0b3JfSUQgJWluJSBub19OQXMpJT4lDQogICMgZmlsdGVyKE1ldHJpYz09J1ZvbHVtZScpJT4lDQogIGRyb3BsZXZlbHMoKSU+JQ0KICBncm91cF9ieShZZWFyRGF5KSU+JQ0KICBhcnJhbmdlKFllYXJEYXksSG91ck1pbiklPiUNCiAgIyBncm91cF9ieShEZXRlY3Rvcl9JRCxEYXRlX1Bvc2l4Y3QpJT4lDQogIG11dGF0ZShjdW1TdW0gPSBjdW1zdW0oTWV0cmljX3ZhbHVlKSwNCiAgICAgICAgIGNvcnJlbGF0aW9uPWxtKGN1bVN1bX5Ib3VyTWluKSRjb2VmZmljaWVudHNbMl0pDQoNCmggPC0gZyU+JQ0KICBncm91cF9ieShEYXRlX1Bvc2l4Y3QpJT4lDQogIHN1bW1hcmlzZShDb3JyZWxhdGlvbj1maXJzdChjb3JyZWxhdGlvbikpJT4lDQogIG11dGF0ZSh3ZGF5ID0gd2Vla2RheXMoRGF0ZV9Qb3NpeGN0KSklPiUNCiAgYXJyYW5nZShEYXRlX1Bvc2l4Y3QpDQoNCnBsb3QgPC0gZ2dwbG90KGcpICsgDQogIGdlb21fbGluZShhZXMoeD1Ib3VyTWluLHk9Y3VtU3VtLGdyb3VwPURhdGVfUG9zaXhjdCxjb2xvcj1XZWVrZGF5KSkgKyANCiAgIyBnZW9tX3J1ZyhhZXMoeD1Ib3VyTWluLHk9Y3VtU3VtKSkgKyANCiAgIyBnZW9tX3Ntb290aChhZXMoeD1Ib3VyTWluLHk9ZGlmZikpKw0KICB0aGVtZV90dWZ0ZSh0aWNrcyA9IEYpICsNCiAgeGxhYigiQ3VtdWxhdGl2ZSBzdW0gZHVyaW5nIHBlYWsgaG91ciIpICsgDQogIHlsYWIoIkN1bVN1bSIpICsgDQogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCh2anVzdD0tMC41KSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHZqdXN0PTEpKSAgDQoNCmdncGxvdGx5KHBsb3QpDQoNCmNvcihnJGN1bVN1bSxnJEhvdXJNaW4pDQoNCmBgYA0KDQoNCg0K